package com.taobao.common.dao.persistence;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.ibatis.SqlMapClientTemplate;
import com.taobao.common.dao.persistence.exception.DAOException;
import com.taobao.common.dao.persistence.exception.DAOOutMaxSizeException;
import com.taobao.common.dao.persistence.exception.DBRouterException;
import com.taobao.common.dao.query.QueryBase;


/**
 * @author xingdian
 */
public class SqlMapBaseDAO {
    private static final int MAX_IDS_SIZE = 100;
    protected static final int PAGE_SIZE = 4;
    protected static final String ALL_DEFAULT_DB_NAME = "all";
    protected static final Object[] NO_ARGUMENTS = new Object[0];
    private static Integer DEFAULT_MAX_RESULT_SET_SIZE = new Integer(10000); //�ܹ����صļ�¼�������ֵ��ȱʡֵ
    protected Log log = LogFactory.getLog(this.getClass());
    private SqlMapBaseDAOSupport sqlDaoBaseSupport;
    private Integer maxResultSetSize = DEFAULT_MAX_RESULT_SET_SIZE; //�ܹ����صļ�¼�������ֵ

    /**
     * ��id����ƴ�� 1,2,3 �������ʽ
     *
     * @param ids
     *
     * @return
     */
    protected String ids2SQLInString(Collection ids) {
        if ((ids == null) || ids.isEmpty()) {
            return "";
        }

        StringBuffer sb = new StringBuffer();

        for (Iterator it = ids.iterator(); it.hasNext();) {
            sb.append(it.next()).append(',');
        }

        sb.deleteCharAt(sb.length() - 1);

        return sb.toString();
    }

    /**
     * @return Returns the maxResultSetSize.
     */
    public Integer getMaxResultSetSize() {
        return maxResultSetSize;
    }

    /**
     * @param maxResultSetSize The maxResultSetSize to set.
     */
    public void setMaxResultSetSize(Integer maxResultSetSize) {
        this.maxResultSetSize = maxResultSetSize;
    }

    /**
     * @return Returns the sqlDaoBaseSupport.
     */
    public SqlMapBaseDAOSupport getSqlDaoBaseSupport() {
        return sqlDaoBaseSupport;
    }

    /**
     * @param sqlDaoBaseSupport The sqlDaoBaseSupport to set.
     */
    public void setSqlDaoBaseSupport(SqlMapBaseDAOSupport sqlDaoBaseSupport) {
        this.sqlDaoBaseSupport = sqlDaoBaseSupport;
    }

    /**
     * ���ڵ���SQL����ִ��ʱ��.
     */
    private void logRunTime(String statementName, String dbName, long runTime) {
        if (log.isDebugEnabled()) {
            log.debug("Sql " + statementName + " executed on " + dbName +
                " databases. Run time estimated: " + runTime + "ms");
        }
    }

    /**
     * ��õײ����ݿ�����
     *
     * @param dbRoute
     *
     * @return ��ݿ�����
     *
     * @throws DAOException
     */
    public final Connection getConnection(DBRoute dbRoute)
        throws DAOException {
        if (dbRoute == null) {
            throw new DAOException("DBRoute ����Ϊ�ա�");
        }

        Map dbMap = this.getSqlMapClient(dbRoute, null);

        if (dbMap.size() != 1) {
            throw new DAOException("����Ψһȷ����ݿ⣡");
        } else {
            Iterator it = dbMap.values().iterator();

            while (it.hasNext()) {
                SqlMapClientTemplate client = (SqlMapClientTemplate) it.next();

                try {
                    return client.getDataSource().getConnection();
                } catch (SQLException e) {
                    throw new DAOException("���ܻ����ݿ����ӡ�");
                }
            }
        }

        return null;
    }

    /**
     * ���·�ɹ��򷵻��ض�����ݿ�ģ��
     */
    private Map getSqlMapClient(DBRoute dr, String sqlId)
        throws DAOException {
        return sqlDaoBaseSupport.getSqlMapTemplates(dr, sqlId);
    }

    private SqlMapClientTemplate getSqlMapTemplate(DBRoute dr)
        throws DBRouterException {
        return sqlDaoBaseSupport.getSqlMapTemplate(dr);
    }

    /**
     * ���·����Ϣ������ݿ�����֡�
     * @param dr
     * @return
     * @throws DBRouterException
     */
    public String getDBName(DBRoute dr) throws DBRouterException {
        return sqlDaoBaseSupport.getDBName(dr, null);
    }

    protected Object executeQueryForObject(String statementName,
        Object parameterObject, DBRoute dr) throws DAOException {
        return executeQueryForObject(statementName, parameterObject, dr, true);
    }

    /**
     * ���ҵ������󣬿���ͳ�Ƽ�¼�ĸ���
     *
     * @param statementName
     * @param parameterObject
     * @param qualification
     * @param isExistsExit ���Ϊ�棬���ڶ��֮�������ֵ�����ء�
     *
     * @return
     *
     * @throws DAOException
     */
    protected Object executeQueryForObject(String statementName,
        Object parameterObject, DBRoute dr, boolean isExistsExit)
        throws DAOException {
        Map dbList = getSqlMapClient(dr, statementName);
        int iTotalCount = 0;
        boolean isInteger = false;

        Iterator it = dbList.keySet().iterator();

        try {
            while (it.hasNext()) {
                String dbName = (String) it.next();
                SqlMapClientTemplate st = (SqlMapClientTemplate) dbList.get(dbName);
                long startTime = System.currentTimeMillis();
                Object returnObject = st.queryForObject(statementName,
                        parameterObject);

                long endTime = System.currentTimeMillis();

                logRunTime(statementName, dbName, endTime - startTime);

                //�����ֵ������
                if (returnObject != null) {
                    //���鵽��DO�Ķ���
                    if (BaseDO.class.isAssignableFrom(returnObject.getClass())) {
                        ((BaseDO) returnObject).setDbRoute(new DBRoute(dbName));

                        return returnObject;
                    }

                    //���鵽��¼�ĸ���
                    if (returnObject instanceof Integer) {
                        iTotalCount += ((Integer) returnObject).intValue();
                        isInteger = true;

                        if ((iTotalCount > 0) && isExistsExit) {
                            break;
                        }
                    } else {
                        return returnObject;
                    }
                } //end if
            } //while
        } catch (DataAccessException dae) {
            throw new DAOException(dae);
        }

        return (isInteger ? new Integer(iTotalCount) : null);
    }

    /**
     * ���ز�ѯ�б?�����Ψһ����һ����ݿ⣬���ڶ��֮��ֱ��ѯ
     *
     * @param statementName
     * @param parameterObject
     * @param dr
     *
     * @return
     *
     * @throws DAOException
     */
    protected List executeQueryForList(String statementName,
        Object parameterObject, DBRoute dr) throws DAOException {
        Map dbMap = null;
        Iterator it = null;
        List resultList = null;

        dbMap = getSqlMapClient(dr, statementName);
        it = dbMap.keySet().iterator();
        resultList = new ArrayList();

        try {
            while (it.hasNext()) {
                long startTime = System.currentTimeMillis();
                String dbName = (String) it.next();
                SqlMapClientTemplate st = (SqlMapClientTemplate) dbMap.get(dbName);
                List list = st.queryForList(statementName, parameterObject);

                genDBRoute(dbName, list);

                long endTime = System.currentTimeMillis();

                logRunTime(statementName, dbName, endTime - startTime);
                resultList.addAll(list);
            }
        } catch (DataAccessException dae) {
            throw new DAOException(dae);
        }

        return resultList;
    }

    /**
     * ����һ��MAP���KEY�Ƿ���DO�е�һ���ֶ����
     *
     * @param statementName
     * @param parameterObject
     * @param dr
     * @param key
     *
     * @return
     *
     * @throws DAOException
     */
    protected Map executeQueryForMap(String statementName,
        Object parameterObject, DBRoute dr, String key)
        throws DAOException {
        Map dbMap = null;
        Iterator it = null;
        Map results = null;

        dbMap = getSqlMapClient(dr, statementName);
        it = dbMap.keySet().iterator();
        results = new HashMap();

        try {
            while (it.hasNext()) {
                long startTime = System.currentTimeMillis();
                String dbName = (String) it.next();
                SqlMapClientTemplate st = (SqlMapClientTemplate) dbMap.get(dbName);

                Map resultMap = st.queryForMap(statementName, parameterObject,
                        key);

                genDBRoute(dbName, resultMap);

                long endTime = System.currentTimeMillis();

                logRunTime(statementName, dbName, endTime - startTime);
                results.putAll(resultMap);
            }
        } catch (DataAccessException dae) {
            throw new DAOException(dae);
        }

        return results;
    }

    /**
     * ������ݿ⣬���Բ���һ����¼��Ҳ����ɾ��һ����¼ ������Ӱ�������
     *
     * @param statementName
     * @param parameterObject
     * @param dr
     *
     * @return �����µļ�¼��
     *
     * @throws DAOException
     */
    protected int executeUpdate(String statementName, Object parameterObject,
        DBRoute dr) throws DAOException {
        Map dbMap = getSqlMapClient(dr, statementName);

        if (dbMap.size() != 1) {
            throw new DAOException("����ȷ����ݿ⣡");
        }

        Iterator it = dbMap.keySet().iterator();
        int updateRows = 0;

        try {
            while (it.hasNext()) {
                String dbName = (String) it.next();
                SqlMapClientTemplate st = (SqlMapClientTemplate) dbMap.get(dbName);
                long startTime = System.currentTimeMillis();

                updateRows = st.update(statementName, parameterObject);

                long endTime = System.currentTimeMillis();

                logRunTime(statementName, dbName, endTime - startTime);
            }

            return updateRows;
        } catch (DataAccessException dae) {
            throw new DAOException(dae);
        }
    }

    /**
     * ����һ����¼
     *
     * @param statementName
     * @param parameterObject
     * @param dr
     *
     * @return �����ӵļ�¼����
     *
     * @throws DAOException
     */
    protected Object executeInsert(String statementName,
        Object parameterObject, DBRoute dr) throws DAOException {
        Map dbMap = getSqlMapClient(dr, statementName);

        if (dbMap.size() != 1) {
            throw new DAOException("����ȷ����ݿ⣡");
        }

        Iterator it = dbMap.keySet().iterator();
        Object back = null;

        try {
            while (it.hasNext()) {
                String dbName = (String) it.next();
                SqlMapClientTemplate st = (SqlMapClientTemplate) dbMap.get(dbName);
                long startTime = System.currentTimeMillis();

                //����Spring template
                back = st.insert(statementName, parameterObject);

                long endTime = System.currentTimeMillis();

                logRunTime(statementName, dbName, endTime - startTime);
            }

            return back;
        } catch (DataAccessException dae) {
            throw new DAOException(dae);
        }
    }

    /**
     * ����ݿ��ҳ��ѯ. ע�⣺select SQL �� count SQL ������mckinley-commom-config�����еģ� conf/bean/persistence.xml
     * �ļ������ã��������ñ�����ͬ. ���ص�DOʵ����java.lang.Comparable����Խ���������. ע�⣺sql������������DO����һ��. �ο�:
     * com.mckinley.biz.dal.member.dataobject.Shop (DO)
     * com.mckinley.biz.dal.member.ibatis.IbatisShopDAO.loadShops() (DAOʵ��)
     *
     * @param selelctStatementName "select .. from ... where ... order by ..."sql����mapping
     * @param countStatementName "select count() from ... where ..."sql����mapping
     * @param query ��ѯ����
     *
     * @return DO����
     *
     * @throws DAOException ����ѯ�Ľ��������ݿ��¼�ܺͣ�����maxResultSetSize�����׳�DAOOutMaxSizeException
     *         ȱʡ��maxResultSetSize�� DEFAULT_MAX_RESULT_SET_SIZE��
     *         �����Ҫ���ã����罫����ֵ��Ϊ5000������DAO������(mckinley-common-config������src/conf/mckinley/bean/biz-dao.xml�ļ�),
     *         ���� "maxResultSetSize" 5000
     */
    protected List getQueryMergeList(String selelctStatementName,
        String countStatementName, QueryBase query) throws DAOException {
        if (selelctStatementName == null) {
            throw new NullPointerException(
                "selelctStatementName can't be null.");
        }

        if (countStatementName == null) {
            throw new NullPointerException("countStatementName can't be null.");
        }

        if (query == null) {
            throw new NullPointerException("query can't be null.");
        }

        //�õ�ÿ����ݿ�ļ�¼��
        Map dbMap = getSqlMapClient(query.getDbRoute(), countStatementName); //count

        // ���õ���dbmap
        int dbCountSTSize = dbMap.size();

        if (dbCountSTSize == 0) {
            throw new IllegalStateException(
                "dbroute config error in count statement :" +
                countStatementName);
        }

        Iterator it = dbMap.keySet().iterator();
        final Map countResult = new LinkedHashMap(dbCountSTSize);
        int totalCount = 0;

        while (it.hasNext()) {
            String dbName = (String) it.next();
            DBRoute dbRoute = new DBRoute();

            dbRoute.setXid(dbName);

            //SqlMapClientTemplate st = (SqlMapClientTemplate) dbMap.get(dbName);
            Integer count = (Integer) this.executeQueryForObject(countStatementName,
                    query, dbRoute);

            countResult.put(dbName, count);
            totalCount += count.intValue();
        }

        //�Լ�¼���С����
        List keys = new ArrayList();

        keys.addAll(countResult.keySet());
        Collections.sort(keys,
            new Comparator() {
                public int compare(Object arg0, Object arg1) {
                    String thisname = (String) arg0;
                    String thatname = (String) arg1;
                    Integer thiscount = (Integer) countResult.get(thisname);
                    Integer thatcount = (Integer) countResult.get(thatname);

                    return thiscount.compareTo(thatcount);
                }
            });

        if (log.isDebugEnabled()) {
            log.debug("result count:" + totalCount);
        }

        //����¼������ڹ涨ֵ�����ټ���ִ��
        if (this.getMaxResultSetSize().intValue() < totalCount) {
            throw new DAOOutMaxSizeException("��ȡ�ļ�¼��:" + totalCount + "̫��,ӦС��" +
                this.getMaxResultSetSize());
        }

        query.setTotalItem(new Integer(totalCount));

        //alahan modify start
        //�����ݿ�������������
        int iOffSet = query.getPageSize().intValue() % dbCountSTSize;

        //ÿ����ݿ���Ҫ��ȡ�ļ�¼��
        int iPerDbSize = query.getPageSize().intValue() / dbCountSTSize;
        int iBegin;
        int iEnd;
        ArrayList dbPerNumList = new ArrayList();
        int iDiffNum = 0; //��ǰ������ݿ������
        int iPageSize = 0; //ÿ����ݿ�ÿҳӦ����ʾ�ļ�¼��
        int iCurrentDBCount = 0;
        int[] isNull = new int[dbCountSTSize]; //ÿ����ݿ��Ƿ�����˼�¼
        int[] iDbCount = new int[dbCountSTSize]; //��ݿ�ļ�¼�ۼ�

        for (int i = 0; i < keys.size(); i++) {
            String tmpDbName = (String) keys.get(i);
            QueryNumber obj = new QueryNumber(tmpDbName, 0, 0);

            iCurrentDBCount = ((Integer) countResult.get(tmpDbName)).intValue() +
                iCurrentDBCount;
            iDbCount[i] = iCurrentDBCount;

            if (i != 0) {
                if (isNull[i - 1] == 0) { //ǰ�����ݿ��Ѿ�û�м�¼���Է��䣬��Ҫ���¼�������ÿ����ݿ�ÿҳ��ʾ��¼��
                    iOffSet = query.getPageSize().intValue() % (dbCountSTSize -
                        i);
                    iPerDbSize = query.getPageSize().intValue() / (dbCountSTSize -
                        i);
                    iDiffNum = 0;
                }
            }

            if (iOffSet > 0) { //ÿҳ��ʾ�ļ�¼���������ӵ�һ�����һֱ��0
                iPageSize = iPerDbSize + 1;
                iBegin = ((query.getCurrentPage().intValue() - 1) * iPageSize) +
                    1;
                iEnd = (iBegin + iPageSize) - 1;

                if (iBegin <= iCurrentDBCount) {
                    obj.startNum = iBegin;
                    iOffSet--;
                } else {
                    iDiffNum = iPerDbSize;
                }
            } else {
                iPageSize = iPerDbSize;
                iBegin = ((query.getCurrentPage().intValue() - 1) * iPageSize) +
                    1;
                iEnd = (iBegin + iPageSize) - 1;

                if (iBegin <= iCurrentDBCount) {
                    obj.startNum = iBegin;
                } else {
                    iDiffNum = iPerDbSize;
                }
            }

            iEnd += iDiffNum; //������¼����ҪҪ����ƫ����

            if (iEnd <= iCurrentDBCount) {
                if (obj.startNum == 0) {
                    obj.endNum = 0;
                } else {
                    obj.endNum = iEnd;
                    iDiffNum = 0;
                }
            } else {
                if (obj.startNum > 0) {
                    obj.endNum = iCurrentDBCount;
                    iDiffNum = iEnd - iCurrentDBCount;
                }
            }

            if (obj.endNum > 0) { //����н�����¼��������ѯ�б�

                if ((i > 0) && (isNull[i - 1] == 0)) {
                    obj.endNum = obj.endNum - iDbCount[i - 1];
                    obj.startNum = obj.startNum - iDbCount[i - 1];
                }

                dbPerNumList.add(obj);
                isNull[i] = 1;
            } else {
                isNull[i] = 0;
            }
        }

        //alahan modify end
        //�õ���¼��
        dbMap = getSqlMapClient(query.getDbRoute(), selelctStatementName); //select
                                                                           // ��ѯ

        int dbSelectSTCount = dbMap.size();

        if (dbSelectSTCount == 0) {
            throw new IllegalStateException(
                "dbroute config error in select statement :" +
                selelctStatementName);
        }

        if (dbSelectSTCount != dbCountSTSize) {
            throw new IllegalStateException(
                "dbroute config error is differ from select statement and count statement");
        }

        it = dbMap.keySet().iterator();

        List resultList = new ArrayList();

        //alahan modify start
        for (int i = 0; i < dbPerNumList.size(); i++) {
            QueryNumber obj = (QueryNumber) dbPerNumList.get(i);
            DBRoute dbRoute = new DBRoute();

            dbRoute.setXid(obj.xid);
            query.setStartRow(obj.startNum);
            query.setEndRow(obj.endNum);

            List list = this.executeQueryForList(selelctStatementName, query,
                    dbRoute);

            resultList.addAll(list);
        }

        //alahan modify end
        if ((resultList == null) || resultList.isEmpty()) {
            return Collections.EMPTY_LIST;
        }

        //�������
        Object dataObject = resultList.get(0);

        if (dataObject instanceof Comparable) {
            if (log.isDebugEnabled()) {
                log.debug("the result of statement:" + selelctStatementName +
                    " need to sort.");
            }

            long beginTime = System.currentTimeMillis();

            Collections.sort(resultList);

            long endTime = System.currentTimeMillis();

            if (log.isDebugEnabled()) {
                log.debug("sort time:" + (endTime - beginTime) + "ms");
            }
        }

        return resultList;
    }

    protected List executeQueryForMergeSortList(String statementName,
        Object parameterObject, int startResult, int maxResults,
        String orderByMethodString, DBRoute dr) throws DAOException {
        Map dbMap = getSqlMapClient(dr, statementName);
        Iterator it = dbMap.keySet().iterator();
        List resultList = new ArrayList();

        if (parameterObject instanceof Map) {
            Map param = (Map) parameterObject;
            param.put("startRow", new Integer(1));
            param.put("endRow", new Integer((maxResults + startResult) - 1));
        }

        //  �ֱ�Ӷ���в�����������
        try {
            while (it.hasNext()) {
                long startTime = System.currentTimeMillis();
                String dbName = (String) it.next();
                SqlMapClientTemplate st = (SqlMapClientTemplate) dbMap.get(dbName);

                // Call spring template
                List list = st.queryForList(statementName, parameterObject);
                long endTime = System.currentTimeMillis();

                logRunTime(statementName, dbName, endTime - startTime);
                genDBRoute(dbName, list);
                resultList.add(list);
            }
        } catch (DataAccessException dae) {
            throw new DAOException(dae);
        }

        //��������
        SqlMapComparator comp = new SqlMapComparator(orderByMethodString);

        try {
            List returnList = comp.sort(resultList, startResult, maxResults);

            if (returnList == null) {
                return new ArrayList();
            }

            return returnList;
        } catch (Exception e) {
            throw new DAOException("����ʧ�ܣ�", e);
        }
    }

    /**
     * �Է��ص�ÿ����¼���÷ֿ���Ϣ
     *
     * @param dbName
     * @param list
     */
    private void genDBRoute(String dbName, List list) {
        //���÷ֿ���Ϣ
        if (list != null) {
            for (int i = 0; i < list.size(); i++) {
                if ((list.get(i) != null) &&
                        BaseDO.class.isAssignableFrom(list.get(i).getClass())) {
                    ((BaseDO) list.get(i)).setDbRoute(new DBRoute(dbName));
                } else {
                    break;
                }
            }
        } //end if
    }

    /**
     * �Է��ص�ÿ����¼���÷ֿ���Ϣ
     *
     * @param dbName
     * @param resultMap
     */
    private void genDBRoute(String dbName, Map resultMap) {
        //���÷ֿ���Ϣ
        if (resultMap != null) {
            Iterator iter = resultMap.values().iterator();

            while (iter.hasNext()) {
                Object element = (Object) iter.next();

                if (BaseDO.class.isAssignableFrom(element.getClass())) {
                    ((BaseDO) element).setDbRoute(new DBRoute(dbName));
                } else {
                    break;
                }
            }
        } //end if
    }

    /**
     * ���List�е�����������ID���з��࣬Ȼ��ֱ���ص���ݿ�����������صĶ���. Ȼ������������Ķ������õ�List��Object��Ӧ��������
     *
     * @param List list
     * @param String statementName �������ID
     * @param String[] strKeyMethodName ID�ķ�������
     * @param String[] strSetObjectMethodName ���ý�����ķ�������
     * @param String strKeyName ����SQL����е�id
     * @param String strRouteMethodName �������ķ�������ΪnullȡstrKeyMethodName��Ϊ�������ķ�����
     *
     * @throws Exception
     * @throws SQLException
     */
    protected void mergeQueryForList(List list, String statementName,
        String[] strKeyMethodName, String[] strSetObjectMethodName,
        String strKeyName, String strRouteMethodName) throws DAOException {
        if ((list == null) || list.isEmpty()) {
            return;
        }

        Map map = getIdsListAndGroupDB(list, statementName, strKeyMethodName,
                strRouteMethodName);
        Map idsMap = (Map) map.get("ids");
        Map objMap = (Map) map.get("obj");
        Iterator dbNodes = idsMap.keySet().iterator();

        while (dbNodes.hasNext()) {
            String dbName = (String) dbNodes.next();

            if (dbName == null) {
                continue;
            }

            SqlMapClientTemplate sqlMap = getSqlMapTemplate(new DBRoute(dbName));
            Set param = (Set) idsMap.get(dbName);

            if (param == null) {
                continue;
            }

            Map retMap = new HashMap();
            Iterator it = param.iterator();

            while (it.hasNext()) {
                Object p = it.next();

                if (p == null) {
                    continue;
                }

                Map retMap1;

                retMap1 = sqlMap.queryForMap(statementName, p, strKeyName);

                retMap.putAll(retMap1);
            }

            Iterator keys = retMap.keySet().iterator();

            while (keys.hasNext()) {
                Object key = keys.next();
                Object val = retMap.get(key);

                if ((val != null) &&
                        BaseDO.class.isAssignableFrom(val.getClass())) {
                    ((BaseDO) val).setDbRoute(new DBRoute(dbName));
                }

                for (int i = 0; i < strSetObjectMethodName.length; i++) {
                    List objList = (List) objMap.get(key + "_" + i);

                    if (objList == null) {
                        continue;
                    }

                    for (int j = 0; j < objList.size(); j++) {
                        setProperty(objList.get(j), strSetObjectMethodName[i],
                            val);
                    }
                }
            }
        }
    }

    protected List mergeQueryForListByIds(String statementName, String[] ids,
        String strKeyName) throws DAOException {
        if (ids == null) {
            return null;
        }

        Map idsMap = groupDB(ids, statementName);

        Iterator dbNodes = idsMap.keySet().iterator();
        BaseDO[] rets = null;

        Map retMap = new HashMap();

        while (dbNodes.hasNext()) {
            String dbName = (String) dbNodes.next();

            if (dbName == null) {
                continue;
            }

            SqlMapClientTemplate sqlMap = getSqlMapTemplate(new DBRoute(dbName));
            Set param = (Set) idsMap.get(dbName);

            if (param == null) {
                continue;
            }

            Iterator it = param.iterator();

            while (it.hasNext()) {
                Object p = it.next();

                if (p == null) {
                    continue;
                }

                Map retMap1;

                retMap1 = sqlMap.queryForMap(statementName, p, strKeyName);

                retMap.putAll(retMap1);
            }
        }

        Iterator keys = retMap.keySet().iterator();
        rets = new BaseDO[ids.length];

        while (keys.hasNext()) {
            String key = (String) keys.next();
            Object val = retMap.get(key);
            
            /*
            if ((val != null) && BaseDO.class.isAssignableFrom(val.getClass())) {
                ((BaseDO) val).setDbRoute(new DBRoute(dbName));
            }
            */
            
            for (int i = 0; i < ids.length; i++) {
                if (key.equals(ids[i])) {
                    rets[i] = (BaseDO) val;
                }
            }
        }

        return Arrays.asList(rets);
    }

    /**
     *
     * @param statementName
     * @param parameterObject
     * @param qualification
     * @param expectedUpdateNumber
     * @return
     * @throws DAOException
     */
    protected int executeUpdateList(String statementName,
        Object parameterObject, DBRoute qualification, int expectedUpdateNumber)
        throws DAOException {
        int updateNumber = 0;
        Map dbMap = null;
        Iterator it = null;

        try {
            updateNumber = 0;
            dbMap = getSqlMapClient(qualification, statementName);
            it = dbMap.keySet().iterator();
        } catch (Exception e) {
            throw new DAOException(
                "Failed while executing update.  Maybe for : " + e, e);
        }

        while (it.hasNext()) {
            long startTime = System.currentTimeMillis();
            String dbName = (String) it.next();
            SqlMapClientTemplate sqlMap = (SqlMapClientTemplate) dbMap.get(dbName);

            //updateNumber += sqlMap.executeUpdate(statementName, parameterObject);
            updateNumber += sqlMap.update(statementName, parameterObject);

            long endTime = System.currentTimeMillis();

            logRunTime(statementName, dbName, endTime - startTime);

            if ((expectedUpdateNumber > 0) &&
                    (updateNumber >= expectedUpdateNumber)) {
                return updateNumber;
            }
        }

        return updateNumber;
    }

    protected boolean isMviewEnabled() {
        return false;
    }

  

    /**
     * ��List��ID���з��ࡣ
     *
     * @param List list
     * @param strKeyMethodName ȡ����ID�ķ�����
     * @param strRouteMethodName �����ֶη�����
     *
     * @throws DAOException
     */
    private Map getIdsListAndGroupDB(List list, String statementName,
        String[] strKeyMethodName, String strRouteMethodName)
        throws DAOException {
        Map ret = new HashMap();
        Map dbMap = new HashMap();
        Map objMap = new HashMap();

        String specifyDbNode = null;

        if (strRouteMethodName != null) {
            if (strRouteMethodName.startsWith("dbNode!")) {
                specifyDbNode = strRouteMethodName.substring("dbNode!".length());
            }
        }

        for (int i = 0; i < list.size(); i++) {
            Object obj = list.get(i);
            String dbNode = specifyDbNode;

            if ((strRouteMethodName != null) && (specifyDbNode == null)) {
                try {
                    dbNode = getDbNodeName(getProperty(obj, strRouteMethodName));
                } catch (DAOException e) {
                    e.printStackTrace();
                } catch (Exception e) {
                    throw new DAOException(e);
                }
            }

            for (int j = 0; j < strKeyMethodName.length; j++) {
                String value;

                try {
                    value = getProperty(obj, strKeyMethodName[j]);
                } catch (Exception e) {
                    throw new DAOException(e);
                }

                if ((value == null) || (value.length() == 0)) {
                    continue;
                }

                if (strRouteMethodName == null) {
                    dbNode = getDbNodeName(value);
                }

                List idsList = null;

                if (dbMap.containsKey(dbNode)) {
                    idsList = (List) dbMap.get(dbNode);
                } else {
                    idsList = new ArrayList();
                    dbMap.put(dbNode, idsList);
                }

                List objList = null;
                String strKey = value + "_" + j;

                if (objMap.containsKey(strKey)) {
                    objList = (List) objMap.get(strKey);
                } else {
                    objList = new ArrayList();
                    objMap.put(strKey, objList);
                    idsList.add(value);
                }

                objList.add(obj);
            }
        }

        Map idsMap = new HashMap();

        if ((specifyDbNode != null) &&
                (ALL_DEFAULT_DB_NAME.compareTo(specifyDbNode) == 0)) {
            Map map = getSqlMapClient(null, statementName);
            Iterator dbNodes = map.keySet().iterator();
            List ll = (List) dbMap.get(ALL_DEFAULT_DB_NAME);

            Set ids = new HashSet();

            if ((ll != null) && (ll.size() > MAX_IDS_SIZE)) {
                int start = 0;
                int size = ll.size();

                while (true) {
                    if (size < MAX_IDS_SIZE) {
                        StringBuffer bf = new StringBuffer();

                        for (int idx = start; idx < ll.size(); idx++) {
                            if (bf.length() > 0) {
                                bf.append(",");
                            }

                            bf.append((String) ll.get(idx));
                        }

                        ids.add(bf.toString());

                        break;
                    } else {
                        StringBuffer bf = new StringBuffer();

                        for (int idx = start; idx < (start + MAX_IDS_SIZE);
                                idx++) {
                            if (bf.length() > 0) {
                                bf.append(",");
                            }

                            bf.append((String) ll.get(idx));
                        }

                        start += MAX_IDS_SIZE;
                        size -= MAX_IDS_SIZE;
                        ids.add(bf.toString());
                    }
                }
            } else {
                ids.add((ll == null) ? null : StringUtil.join(ll.iterator(), ','));
            }

            while (dbNodes.hasNext()) {
                idsMap.put((String) dbNodes.next(), ids);
            }
        } else {
            Iterator dbNodes = dbMap.keySet().iterator();

            while (dbNodes.hasNext()) {
                String key = (String) dbNodes.next();
                List ll = (List) dbMap.get(key);

                Set ids = new HashSet();

                if ((ll != null) && (ll.size() > MAX_IDS_SIZE)) {
                    int start = 0;
                    int size = ll.size();

                    while (true) {
                        if (size < MAX_IDS_SIZE) {
                            StringBuffer bf = new StringBuffer();

                            for (int idx = start; idx < ll.size(); idx++) {
                                if (bf.length() > 0) {
                                    bf.append(",");
                                }

                                bf.append((String) ll.get(idx));
                            }

                            ids.add(bf.toString());

                            break;
                        } else {
                            StringBuffer bf = new StringBuffer();

                            for (int idx = start; idx < (start + MAX_IDS_SIZE);
                                    idx++) {
                                if (bf.length() > 0) {
                                    bf.append(",");
                                }

                                bf.append((String) ll.get(idx));
                            }

                            start += MAX_IDS_SIZE;
                            size -= MAX_IDS_SIZE;
                            ids.add(bf.toString());
                        }
                    }
                } else {
                    ids.add((ll == null) ? null
                                         : StringUtil.join(ll.iterator(), ','));
                }

                idsMap.put(key, ids);
            }
        }

        ret.put("ids", idsMap);
        ret.put("obj", objMap);

        return ret;
    }

    private Map groupDB(String[] idArray, String statementName)
        throws DAOException {
        Map dbMap = new HashMap();

        String specifyDbNode = null;

        for (int i = 0; i < idArray.length; i++) {
            String dbNode = specifyDbNode;

            String value = idArray[i];

            if ((value == null) || (value.length() == 0)) {
                continue;
            }

            dbNode = getDbNodeName(value);

            List idsList = null;

            if (dbMap.containsKey(dbNode)) {
                idsList = (List) dbMap.get(dbNode);
            } else {
                idsList = new ArrayList();
                dbMap.put(dbNode, idsList);
            }

            if (!idsList.contains(value)) {
                idsList.add(value);
            }
        }

        Map idsMap = new HashMap();

        if ((specifyDbNode != null) &&
                (ALL_DEFAULT_DB_NAME.compareTo(specifyDbNode) == 0)) {
            Map map = getSqlMapClient(null, statementName);
            Iterator dbNodes = map.keySet().iterator();
            List ll = (List) dbMap.get(ALL_DEFAULT_DB_NAME);

            Set ids = new HashSet();

            if ((ll != null) && (ll.size() > MAX_IDS_SIZE)) {
                int start = 0;
                int size = ll.size();

                while (true) {
                    if (size < MAX_IDS_SIZE) {
                        StringBuffer bf = new StringBuffer();

                        for (int idx = start; idx < ll.size(); idx++) {
                            if (bf.length() > 0) {
                                bf.append(",");
                            }

                            bf.append((String) ll.get(idx));
                        }

                        ids.add(bf.toString());

                        break;
                    } else {
                        StringBuffer bf = new StringBuffer();

                        for (int idx = start; idx < (start + MAX_IDS_SIZE);
                                idx++) {
                            if (bf.length() > 0) {
                                bf.append(",");
                            }

                            bf.append((String) ll.get(idx));
                        }

                        start += MAX_IDS_SIZE;
                        size -= MAX_IDS_SIZE;
                        ids.add(bf.toString());
                    }
                }
            } else {
                ids.add((ll == null) ? null : StringUtil.join(ll.iterator(), ','));
            }

            while (dbNodes.hasNext()) {
                idsMap.put((String) dbNodes.next(), ids);
            }
        } else {
            Iterator dbNodes = dbMap.keySet().iterator();

            while (dbNodes.hasNext()) {
                String key = (String) dbNodes.next();
                List ll = (List) dbMap.get(key);

                Set ids = new HashSet();

                if ((ll != null) && (ll.size() > MAX_IDS_SIZE)) {
                    int start = 0;
                    int size = ll.size();

                    while (true) {
                        if (size < MAX_IDS_SIZE) {
                            StringBuffer bf = new StringBuffer();

                            for (int idx = start; idx < ll.size(); idx++) {
                                if (bf.length() > 0) {
                                    bf.append(",");
                                }

                                bf.append((String) ll.get(idx));
                            }

                            ids.add(bf.toString());

                            break;
                        } else {
                            StringBuffer bf = new StringBuffer();

                            for (int idx = start; idx < (start + MAX_IDS_SIZE);
                                    idx++) {
                                if (bf.length() > 0) {
                                    bf.append(",");
                                }

                                bf.append((String) ll.get(idx));
                            }

                            start += MAX_IDS_SIZE;
                            size -= MAX_IDS_SIZE;
                            ids.add(bf.toString());
                        }
                    }
                } else {
                    ids.add((ll == null) ? null
                                         : StringUtil.join(ll.iterator(), ','));
                }

                idsMap.put(key, ids);
            }
        }

        return idsMap;
    }

    /**
     * ��Object����methodName����ȡ��һ��ֵ
     *
     * @param Object object ����
     * @param String methodName ������
     *
     * @return String ֵ
     */
    private String getProperty(Object object, String methodName)
        throws Exception {
        Class claz = object.getClass();
        Method method = claz.getMethod(methodName, new Class[0]);
        Object value = method.invoke(object, NO_ARGUMENTS);

        return ((value == null) ? null : value.toString());
    }

    /**
     * ��methodName����value��object��
     *
     * @param Object object ����
     * @param String methodName ������
     * @param Object value ֵ
     */
    private void setProperty(Object object, String methodName, Object value)
        throws DAOException {
        if ((value == null) || (object == null) || (methodName == null)) {
            return;
        }

        try {
            Class claz = object.getClass();
            Class[] paramType = new Class[1];

            paramType[0] = value.getClass();

            Method method = claz.getMethod(methodName, paramType);
            Object[] params = new Object[1];

            params[0] = value;
            method.invoke(object, params);
        } catch (Exception e) {
            throw new DAOException(e);
        }
    }

    /**
     * ȡ�ýڵ������
     *
     * @return
     *
     * @throws DAOException
     */
    public String getDbNodeName(String node) throws DAOException {
        DBRoute dr = new DBRoute();

        dr.setUserId(node);

        Map map = getSqlMapClient(dr, null);

        if (map != null) {
            Iterator it = map.keySet().iterator();

            while (it.hasNext()) {
                String element = (String) it.next();

                return element;
            }
        }

        return null;
    }

    class QueryNumber {
        public String xid;
        public int startNum;
        public int endNum;

        public QueryNumber(String xid, int startNum, int endNum) {
            this.xid = xid;
            this.startNum = startNum;
            this.endNum = endNum;
        }
    }
    
    /**
     * wanjian 2006.03.10
     * @param selelctStatementName
     * @param countStatementName
     * @param query
     * @return List
     * @throws DAOException
     * 
     * ����֪DBRoute������£���ѯһ��������
     */
    protected List getQueryListOfOneDB(String selelctStatementName,
            String countStatementName, QueryBase query) throws DAOException {
            if (selelctStatementName == null) {
                throw new NullPointerException(
                    "selelctStatementName can't be null.");
            }

            if (countStatementName == null) {
                throw new NullPointerException("countStatementName can't be null.");
            }

            if (query == null) {
                throw new NullPointerException("query can't be null.");
            }
            
            Integer iCount = (Integer)this.executeQueryForObject(countStatementName,query,query.getDbRoute());
            query.setTotalItem(iCount);
            return this.executeQueryForList(selelctStatementName,query,query.getDbRoute());
    }    
    
    /**
     * ��������һ����¼
     * @param countStatementName
     * @param insertStatementName
     * @param updateStatementName
     * @param parameterObject
     * @param dr
     * @return Object
     * @throws DAOException
     */
    protected Object executeInsertOrUpdate(String countStatementName, String insertStatementName, 
    		String updateStatementName, Object parameterObject, DBRoute dr) throws DAOException {
    	Integer count = (Integer)this.executeQueryForObject(countStatementName, parameterObject, dr);
    	if (null != count && count.intValue() > 0) {
    		int u = this.executeUpdate(updateStatementName, parameterObject, dr);
    		return new Integer(u);
    	}
    	return this.executeInsert(insertStatementName, parameterObject, dr);
    }

}

